import apis from './apis'
import { isObjectLike } from '../plugins/utils'

const vmHttp = weex.requireModule('vmHttp')
const bmTool = weex.requireModule('bmTool')
const bmModal = weex.requireModule('bmModal')
const baseUrl = weex.config.env.request

const getTroubleQuery = (tableName, queryList = {}, order = {}, empty = true, fields = '') => {
  const directKey = ['index', 'size', 'search', 'link', 'groupFields', 'fields', 'groupFunc']
  const queryArr = []
  Object.keys(queryList).forEach(key => {
    const value = String(queryList[key]) || ''
    if (value || !empty) {
      if (directKey.indexOf(key) > -1) {
        queryArr.push(`${key}=${String(value)}`)
      } else {
        queryArr.push(getQueryString(tableName, key, value))
      }
    }
  })
  Object.keys(order).forEach(o => {
    queryArr.push(getOrderString(tableName, o, order[o]))
  })
  if (fields) {
    queryArr.push(...getFieldsArray(fields))
  }
  return queryArr.length ? `${queryArr.join('&')}` : ''
}

const getQueryString = (tableName, key, value = '') => {
  const codeKey = ['address_code', 'farm_address_code', 'category_id', 'goods_category_id']
  const arr = key.split('.')

  // ?qkey="tableName.tkey".cond(value)
  let qkey = 'where'
  let tkey = ''
  let cond = 'like'

  const len = arr.length
  switch (len) {
    case 1: // {'tkey': value}
      tkey = arr[0]
      break
    case 2: // {'tkey.cond': value}
      tkey = arr[0]
      cond = arr[1]
      break
    case 2: // {'qkey.tkey.cond': value}
      qkey = arr[0]
      tkey = arr[1]
      cond = arr[2]
      break
    default:
      break
  }

  switch (cond) {
    case 'like':
      value = codeKey.indexOf(tkey) > -1 ? `${value.replace(/0+$/g, '')}%` : `%${value}%`
      break
    case 'endlike':
      cond = 'like'
      value = `${value}%`
      break
    case 'codelike':
      cond = 'like'
      value = `${value.replace(/0+$/g, '')}%`
      break
    // case 'start':
    //   cond = 'like'
    //   value = `%${value}`
    //   break
    default:
      break
  }

  return `${qkey}=${encodeURIComponent(`"${tableName}.${tkey}".${cond}(${value})`)}`
}

const getOrderString = (tableName, key, value = 'desc') => {
  return `order=${encodeURIComponent(`"${tableName}.${key}"(${value})`)}`
}

const getFieldsArray = (fields) => {
  if (typeof fields === 'string') {
    return [`fields=${fields}`]
  } else if (Object.prototype.toString.call(fields) === '[object Array]') {
    const res = []
    for (const field of fields) {
      res.push(`fields=${field}`)
    }
    return res
  }
  return []
}

export const toQueryString = obj => {
  let ret = []
  for (let key in obj) {
    key = encodeURIComponent(key)
    let value = obj[key] || ''
    ret.push(`${key}=${String(value)}`)
  }
  return ret.length ? `?${ret.join('&')}` : ''
}

export const requestHandler = (options, next) => {
  console.log(JSON.stringify(options))
  // console.log('request-opts', options)
  next()
}

export const responseHandler = (options, resData, resolve, reject) => {
  const { status, errorMsg, data, header } = resData
  if (status !== 200) {
    console.log(`invoke error message: ${errorMsg}(status: ${status})`)

    const defaultErrorMsg = options.errorMsg
    const errData = errorHandler(resData, defaultErrorMsg)

    console.log(JSON.stringify(options))
    if (defaultErrorMsg) {
      console.log(JSON.stringify(errData))
      bmModal.toast({ message: errData.gmMessage })
    }

    reject(errData)
  } else {
    resolve(data)
  }
}

/**
 * 全局错误处理
 * 
 * @param {Object} resData 错误信息
 */
const errorHandler = (resData, defaultErrorMsg = '') => {
  // 全局处理信息追加到错误信息中(兼容之前的错误处理)
  const assignData = (obj) => {
    return Object.assign(obj, resData)
  }

  const { status, errorMsg } = resData

  if (typeof errorMsg === 'string') {
    // 超时
    if (/(^connect timed out)|(^timeout)/i.test(errorMsg)) {
      return assignData({
        gmHandler: 2,
        gmMessage: '请求网络超时！'
      })
    }

    // 检查网络状态
    const netStatus = bmTool.networkStatus()
    if (netStatus === 'NOT_REACHABLE') {
      return assignData({
        gmHandler: 1,
        gmMessage: '获取数据失败，请检查你的网络是否可用！'
      })
    }
  }

  // httpError
  if (status >= 500 && status < 600) {
    return assignData({
      gmHandler: 3,
      gmMessage: `${status}: 服务器异常，请稍后再试！`
    })
  }

  if (status === 405) {
    return assignData({
      gmHandler: 3,
      gmMessage: `${status}: 请求方法不对！`
    })
  }

  if (status === 404) {
    return assignData({
      gmHandler: 3,
      gmMessage: `${status}: 请求地址不存在！`
    })
  }

  if (status === 403) {
    return assignData({
      gmHandler: 3,
      gmMessage: `${status}: 没有权限！`
    })
  }

  if (status === 401) {
    return assignData({
      gmHandler: 3,
      gmMessage: `${status}: 没有登录或登录过期！`
    })
  }

  let httpErrorDes = ''
  if (isObjectLike(errorMsg)) {
    httpErrorDes = errorMsg.error_description || errorMsg.errorDescription || errorMsg.message || ''
  }
  if (defaultErrorMsg && httpErrorDes) {
    httpErrorDes = `(${httpErrorDes})`
  }
  return assignData({
    gmHandler: 0,
    gmMessage: `${defaultErrorMsg}${httpErrorDes}`
  })
}

const pathFormater = (name, params) => {
  let _path = apis[name]
  const matcher = _path.match(/[^{][a-zA-Z0-9]+(?=\})/g)
  if (matcher && matcher.length) {
    matcher.forEach(item => {
      if (!params[item]) throw new Error(`you are using dynamic params, but ${item} not existed in your params`)
      _path = _path.replace(`{${item}}`, params[item] || 'undefined')
    })
  }
  return _path
}

const handleFetch = (options, resolve, reject) => {
  requestHandler(options, () => { handleAxios(options, resolve, reject) })
}

const handleAxios = (options, resolve, reject) => {
  const { url, name, params, data, method, header } = options || {}
  vmHttp.fetch(JSON.stringify({
    url: url || `${baseUrl}${name && pathFormater(name, params)}`,
    data: data || {},
    method: (method && method.toUpperCase()) || 'GET',
    header: header || {},
    timeout: 30000
  }), resData => {
    responseHandler(options, resData, resolve, reject)
  })
}

const Api = Object.create(null)
Api.install = Vue => {
  Vue.prototype.$api = {
    // 读取数据
    getData: ({ server, table, params, order, empty, fields, linkParams, errorMsg }) => {
      let query = getTroubleQuery(table, params, order, empty, fields)
      if (params && params.link) {
        const linkQuery = getTroubleQuery(params.link, linkParams)
        query += query ? `&${linkQuery}` : linkQuery
      }

      const url = `${baseUrl}/api/${server}/${table}?${query}`
      return new Promise((resolve, reject) => {
        handleFetch({ url, errorMsg }, resolve, reject)
      })
    },
    getDataById: ({ server, table, id, link, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          url: `${baseUrl}/api/${server}/${table}/${id}${link ? `?link=${link}` : ''}`,
          errorMsg
        }, resolve, reject)
      })
    },
    getDataQuery: ({ server, table, query, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          url: `${baseUrl}/api/${server}/${table}${query}`,
          errorMsg
        }, resolve, reject)
      })
    },

    // 更新数据
    updateData: ({ server, table, id, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'PUT',
          url: `${baseUrl}/api/${server}/${table}/${id}`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },
    updateQuery: ({ server, table, params, data, empty, errorMsg }) => {
      const query = getTroubleQuery(table, params, {}, empty)
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'PUT',
          url: `${baseUrl}/api/${server}/${table}/where/?${query}`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },
    updateBatch: ({ server, table, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'PUT',
          url: `${baseUrl}/api/${server}/${table}/batch/`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },

    // 添加数据
    addData: ({ server, table, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'POST',
          url: `${baseUrl}/api/${server}/${table}`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },
    addBatch: ({ server, table, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'POST',
          url: `${baseUrl}/api/${server}/${table}/batch/`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },

    // 删除数据
    deleteData: ({ server, table, id, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'DELETE',
          url: `${baseUrl}/api/${server}/${table}/${id}`,
          errorMsg
        }, resolve, reject)
      })
    },
    deleteDataQuery: ({ server, table, query, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'DELETE',
          url: `${baseUrl}/api/${server}/${table}?${query}`,
          errorMsg
        }, resolve, reject)
      })
    },
    deleteDataBody: ({ server, table, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'DELETE',
          url: `${baseUrl}/api/${server}/${table}`,
          data: { data },
          errorMsg
        }, resolve, reject)
      })
    },

    // 关联操作
    relatedBatch: ({ server, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'POST',
          url: `${baseUrl}/api/${server}/related/batch/`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },
    relatedDelete: ({ server, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'DELETE',
          url: `${baseUrl}/api/${server}/related/delete/`,
          data,
          errorMsg
        }, resolve, reject)
      })
    },
    relatedUpdate: ({ server, data, errorMsg }) => {
      return new Promise((resolve, reject) => {
        handleFetch({
          method: 'PUT',
          url: `${baseUrl}/api/${server}/related/record/`,
          data,
          errorMsg
        }, resolve, reject)
      })
    }
  }

  Vue.prototype.$http = ({ url, name, params, data, method, header, errorMsg }) => {
    return new Promise((resolve, reject) => {
      handleFetch({ url, name, params, data, method, header, errorMsg }, resolve, reject)
    })
  }
}

Vue.use(Api)
